import typing
from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtCore import pyqtSignal, QObject  # 添加这些导入
import sys
from ui.Ui_serial_assist_widget import Ui_SerialAssist
import serial.tools.list_ports
from serial_setting import SerialSettingDialog
from PyQt5.QtWidgets import QDialog
import serial
import threading

# 创建一个用于线程间通信的信号类
class SerialSignals(QObject):
    data_received = pyqtSignal(str)
    status_update = pyqtSignal(bool)

class Serial_MyWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__()
        self.ui = Ui_SerialAssist()  # ui创建对象
        self.ui.setupUi(self)
        self.init_ui()
        self.pport = None
        self.botelv = None
        self.my_data = None
        self.my_stop_bit = None
        self.ser = None
        self.serial_thread = None
        self.running = False
        
        # 创建信号对象
        self.signals = SerialSignals()
        # 连接信号到槽函数
        self.signals.status_update.connect(self.update_ui)
        self.signals.data_received.connect(self.append_data)

    def init_ui(self):
        self.list_serial_ports()
        self.ui.pushButton.clicked.connect(self.list_serial_ports)
        self.ui.btn_setting.clicked.connect(self.open_setting)
        self.ui.pushButton_2.clicked.connect(self.on_connect)
        
        # 添加清空接收区和发送区的功能
        self.ui.btn_clear_recv.clicked.connect(self.clear_receive_area)
        self.ui.btn_clear_send.clicked.connect(self.clear_send_area)
        
        # 添加发送数据的功能
        self.ui.btn_send.clicked.connect(self.send_data)

    def clear_receive_area(self):
        """清空接收区"""
        self.ui.textBrowser_recv.clear()

    def clear_send_area(self):
        """清空发送区"""
        self.ui.textEdit_send.clear()

    def send_data(self):
        """发送数据"""
        if self.ser is None or not self.ser.is_open:
            print("请先连接串口")
            return
            
        # 获取要发送的文本
        text_to_send = self.ui.textEdit_send.toPlainText()
        if not text_to_send:
            print("发送内容为空")
            return
            
        try:
            # 添加换行符（根据常见串口助手的行为）
            if not text_to_send.endswith('\n'):
                text_to_send += '\n'
                
            # 发送数据
            self.ser.write(text_to_send.encode('utf-8'))
            print(f"已发送: {text_to_send.strip()}")
        except Exception as e:
            print(f"发送数据时出错: {e}")

    def on_connect(self):
        if self.ser is None or not self.ser.is_open:
            self.serial_open()
        else:
            self.serial_close()

    def run_serial(self):
        try:
            self.ser = serial.Serial(
                self.pport,
                int(self.botelv),
                int(self.my_data),
                # 添加超时设置，避免阻塞
                timeout=1
            )
            
            if not self.ser.is_open:
                print("串口连接失败")
                self.signals.status_update.emit(False)
                return
                
            print("串口连接成功")
            self.signals.status_update.emit(True)
            
            # 使用运行标志控制循环
            while self.running:
                try:
                    # 获取串口数据
                    if self.ser.in_waiting:
                        data = self.ser.readline().decode('utf-8').strip()
                        if data:
                            # 通过信号发送数据到主线程
                            self.signals.data_received.emit(data)
                except Exception as e:
                    print(f"读取串口数据错误: {e}")
                    break
                    
        except Exception as e:
            print(f"串口操作错误: {e}")
            self.signals.status_update.emit(False)
        finally:
            if self.ser and self.ser.is_open:
                self.ser.close()
            self.ser = None
            self.running = False
            self.signals.status_update.emit(False)

    def append_data(self, data):
        # 在主线程中更新UI
        self.ui.textBrowser_recv.append(data)  # 假设你有一个text用于显示数据

    def update_ui(self, connected):
        # 在主线程中更新UI
        if connected:
            self.ui.pushButton_2.setText("断开连接")
        else:
            self.ui.pushButton_2.setText("连接设备")

    def serial_open(self):
        if self.pport is None or self.botelv is None:
            print("请先选择端口和波特率")
            return
            
        self.running = True
        self.serial_thread = threading.Thread(target=self.run_serial)
        self.serial_thread.daemon = True  # 设置为守护线程
        self.serial_thread.start()

    def serial_close(self):
        self.running = False
        if self.ser and self.ser.is_open:
            self.ser.close()
        if self.serial_thread and self.serial_thread.is_alive():
            self.serial_thread.join(timeout=1.0)  # 等待线程结束

    def open_setting(self):
        print("打开设置")
        self.dialog = SerialSettingDialog(self)
        rst = self.dialog.exec_()  # 以模态对话框方式显示 ,阻塞父窗口
        if rst == QDialog.Accepted:
            print("用户点击了确定按钮")
            print(f"波特率：{self.dialog.acceptbaud},数据位：{self.dialog.data_bit},停止位：{self.dialog.stop_bit}")
            self.botelv = self.dialog.acceptbaud
            self.my_data = self.dialog.data_bit
            self.my_stop_bit = self.dialog.stop_bit
            self.ui.cb_baod.setCurrentIndex(self.ui.cb_baod.findText(self.dialog.acceptbaud))
        else:
            print("用户取消了设置")

    def list_serial_ports(self):
        # 获取所有串口设备信息
        self.ports = serial.tools.list_ports.comports()
        device_names = [port.device for port in self.ports]  # 直接获取设备名称
        
        if not device_names:
            print("没有找到串口设备。")
            device_names = ["无可用设备"]
        
        self.ui.cb_deveic.clear()
        self.ui.cb_deveic.addItems(device_names)
        
        if device_names and device_names[0] != "无可用设备":
            self.pport = device_names[0]  # 默认选择第一个设备

    def closeEvent(self, event):
        # 窗口关闭时确保串口关闭和线程退出
        self.serial_close()
        event.accept()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = Serial_MyWidget()
    w.show()    
    sys.exit(app.exec_())